import java.util.ArrayList;
import java.util.Random;

import javax.sound.midi.MetaEventListener;
import javax.sound.midi.MetaMessage;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.Sequence;
import javax.sound.midi.Sequencer;
import javax.sound.midi.Track;

public class HeartModel implements IHeartModel, MetaEventListener, Runnable {
	
	Sequencer sequencer;
	Sequence  sequence;
	Track     track;
	Thread	  thread;
	
	private ArrayList beatObservers = new ArrayList();
	private ArrayList bpmObservers = new ArrayList();
	private int bpm = 90;
	private boolean bHeartbeat = false;
	
	public void initialize()
	{
		setupMidi();
		buildTrackAndStart();
		
		thread = new Thread(this);
		thread.start();
	}
	
	public void run()
	{
		Random random = new Random(System.currentTimeMillis());
		while (true)
		{
			int heartbeat = random.nextInt(200);
			bpm = heartbeat;
			
			try
			{
				Thread.sleep(1000);
			}
			catch (Exception e)
			{
				
			}
			notifyBPMObservers();
		}
	}
	public void meta(MetaMessage message)
	{
		if (message.getType() == 47)
		{
			beatEvent();
			sequencer.start();
			setBPM(getBPM());
		}
	}
	
	public void setupMidi()
	{
		try
		{
			sequencer = MidiSystem.getSequencer();
			sequencer.open();
			sequencer.addMetaEventListener(this);
			sequence = new Sequence(Sequence.PPQ, 4);
			track = sequence.createTrack();
			sequencer.setTempoInBPM(getBPM());
		}
		catch (Exception e)
		{
		
		}
	}
		
	public void buildTrackAndStart()
	{
		int [] trackList = { 35, 0, 46, 0 };
		
		sequence.deleteTrack(null);
		track = sequence.createTrack();
		
	//	makeTracks(trackList);
	//	track.add(makeEvent(192, 9, 1, 0, 4));
	}
	
	
	public void on()
	{
	//	sequencer.start();
		setBPM(90);	// default
	}
	public void off()
	{
	//	sequencer.stop();
		setBPM(0);
	}
	
	public void setBPM(int bpm)
	{
		this.bpm = bpm;
		sequencer.setTempoInBPM(getBPM());
		notifyBPMObservers();
	}
	public int getBPM()
	{
		return bpm;
	}
	
	private void beatEvent()
	{
		notifyBeatObservers();
		notifyBPMObservers();
	}
	
	
	private void notifyBPMObservers()
	{
		for (int i= 0 ; i < bpmObservers.size() ; i++)
		{
			IBPMObserver o = (IBPMObserver)bpmObservers.get(i);
			o.updateBPM();
		}
	}
	
	private void notifyBeatObservers()
	{
		for (int i= 0 ; i < beatObservers.size() ; i++)
		{
			IBeatObserver o = (IBeatObserver)beatObservers.get(i);
			o.updateBeat();
		}
	}
	
	public int getHeartRate()
	{
		return bpm;
	}
	
	public void registerObserver(IBeatObserver o)
	{
		beatObservers.add(o);
	}
	public void removeObserver(IBeatObserver o)
	{
		beatObservers.remove(o);
	}
	public void registerObserver(IBPMObserver o)
	{
		bpmObservers.add(o);
	}
	public void removeObserver(IBPMObserver o)
	{
		bpmObservers.remove(o);
	}
}
